package dancrawler.server;

import dancrawler.client.GameListener;
import java.util.*;
import java.util.Arrays;

/**
 * A game world for testing purposes.
 */
public class WorldGenerator implements Gameworld {

    private Tile[][] tiles;
    int dp = 1;
    private int focusX = 0;
    private int focusY = 0;
    //private GameObject player;
    private List<GameListener> glis;
    private List<Move> updates;
    private GameObjectRegister register;
    private int gridSize = 100;
    private int cellSize = 10;
    private int num = (gridSize * gridSize) / 2;
    private int cellAmount = ((gridSize / cellSize) * (gridSize / cellSize));
    private int cellAmountDirection = (gridSize / cellSize);
    private RoomGenerator roomGen;
    private Cell[] cells;
    private int xCoordCount = 0;
    private int yCoordCount = 0;
    private Tile startTile;
    private GameHandler gh;
    private int nplayers = 0;
    private Gameworld next;

    public WorldGenerator(GameObjectRegister reg, GameHandler gh, int dp) {
        glis = new LinkedList<>();
        register = reg;
this.gh= gh;
this.dp = dp;
        tiles = new Tile[gridSize][gridSize];

        updates = new ArrayList<>();

        focusX = num / 2;
        focusY = num / 2;

        buildWorld();
    }
    public void setGameHandler(GameHandler gh)
    {
        this.gh = gh;
    }
    public void buildWorld() {

        cells = new Cell[cellAmount];
        roomGen = new RoomGenerator(cellSize, cellSize, cellAmount, 1);
        roomGen.generateRooms(cellAmount);



        Random rgen = new Random(System.currentTimeMillis());

        for (int i = 0; i < cells.length; i++) {
            cells[i] = new Cell(cellSize, cellSize, cellAmount, i / cellAmountDirection, i % cellAmountDirection);
            if (rgen.nextInt(100) < 90) {
                cells[i].setCellTiles(roomGen.getRooms()[i].getRoom());
                roomGen.getRooms()[i].getConnector().add(cells[i]);
                cells[i].setUsed();
            } else {
                cells[i].setEmptyCell();
            }
        }
        populateTiles();
    }

    public void populateTiles() {


        int x = 0;
        int y = 0;

        connectCells();

        for (int g = 0; g < cells.length;) {
            for (int k = 0; k < cellSize; k++) {
                for (int h = 0; h < cellSize; h++) {
                    tiles[x + k][y + h] = cells[g].getCellTiles()[k][h];

                    if (cells[g].getCellTiles()[k][h] != null) {
                        tiles[x + k][y + h] = new Tile(x + k,
                                y + h, cells[g].getCellTiles()[k][h].getType(),
                                cells[g].getCellTiles()[k][h].collides(),this);
                        for(GameObject o:cells[g].getCellTiles()[k][h].getContents())
                        {
                            tiles[x + k][y + h].add(o);
                        }
                        if(cells[g].getCellTiles()[k][h]==startTile)
                        {
                            startTile = tiles[x + k][y + h];
                        }
                    }
                }
            }

            y += cellSize;
            g++;
            if (g % cellAmountDirection == 0) {
                x += cellSize;
                y = 0;
            }
        }
    }

    public Tile probeTile(Cell c, int x, int y) {
        if (x < 0 || y < 0 || x > c.getCellTiles().length || y > c.getCellTiles()[x].length) {
            return null;
        } else {
            return c.getCellTiles()[x][y];
        }
    }

    public GeneratedTile findTile(Cell c) {

        ArrayList<GeneratedTile> t = new ArrayList<>();
        Random rgen = new Random(System.currentTimeMillis());

        for (int i = 0; i < c.getCellTiles().length; i++) {
            loop:
            for (int j = 0; j < c.getCellTiles()[i].length; j++) {
                if (c.getCellTiles()[i][j] != null) {
                    if (!c.getCellTiles()[i][j].collides() && c.getCellTiles()[i][j].isConnectedTo(c)) {
                        for(GameObject o :c.getCellTiles()[i][j].getContents() )
                        {
                            if(o.collides())
                            {
                                continue loop;
                            }
                        }
                        t.add(c.getCellTiles()[i][j]);
                    }
                }
            }
        }
        try {
            return t.get(rgen.nextInt(t.size()));
        } catch (IllegalArgumentException e) {
            return null;
        }
    }

    public void connectCells() {

        ArrayList<Cell> c = new ArrayList<>();
        Random rgen = new Random(System.currentTimeMillis());

        for (int i = 0; i < cells.length; i++) {
            if (cells[i].isUsed()) {
                c.add(cells[i]);
            }
        }
        

        Cell cell = c.get(rgen.nextInt(c.size()));
        cell.setConnected();
        c.remove(cell);
        Tile start = findTile(cell);
        startTile = start;
        Tile end = findTile(c.get(rgen.nextInt(c.size())));
        end.add(new Portal("stairDN",this));
        while (!c.isEmpty()) {
            Cell target = c.get(rgen.nextInt(c.size()));
            int cnum = rgen.nextInt(9);
            if(cnum > 4)
            {
                cnum-=4;
                for(int i = 0; i <= cnum;i++)
                {
                    Tile tp = findTile(target);
                    Creature creature = new Creature("worgS", gh, this);
                    creature.setLevel((int)(Math.pow(1.125,dp)));
                    register.registerGameObject(creature);
                    tp.add(creature);
                    gh.getAIController().registerBot(creature);
                }
            }
            if (target.isConnected()) {
                c.remove(target);
                continue;
            }
            GeneratedTile t = findTile(cell);
            Cell temp = cell;
            GeneratedTile t3 = findTile(target);
            while (true) {
                if (target == temp) {

                    if (t.getX() < t3.getX()) {
                        t = buildFloor(temp, target, t.getX() + 1, t.getY(), t);
                    } else if (t.getX() > t3.getX()) {
                        t = buildFloor(temp, target, t.getX() - 1, t.getY(), t);
                    } else {
                        if (t.getY() < t3.getY()) {
                            t = buildFloor(temp, target, t.getX(), t.getY() + 1, t);
                        } else if (t.getY() > t3.getY()) {
                            t = buildFloor(temp, target, t.getX(), t.getY() - 1, t);
                        }
                    }
                } else if (target.getxCellCoord() == temp.getxCellCoord()) {
                    if (target.getyCellCoord() < temp.getyCellCoord()) {
                        if (t.getY() == 0) {
                            temp = cells[temp.getxCellCoord() * cellAmountDirection + (temp.getyCellCoord() - 1)];
                            t = buildFloor(temp, target, t.getX(), cellSize - 1, t);
                        } else {
                            t = buildFloor(temp, target, t.getX(), t.getY() - 1, t);
                        }
                    } else {
                        if (t.getY() == cellSize - 1) {
                            temp = cells[temp.getxCellCoord() * cellAmountDirection + temp.getyCellCoord() + 1];
                            t = buildFloor(temp, target, t.getX(), 0, t);
                        } else {
                            t = buildFloor(temp, target, t.getX(), t.getY() + 1, t);
                        }
                    }
                } else if (target.getxCellCoord() < temp.getxCellCoord()) {
                    if (t.getX() == 0) {
                        temp = cells[(temp.getxCellCoord() - 1) * cellAmountDirection + temp.getyCellCoord()];
                        t = buildFloor(temp, target, cellSize - 1, t.getY(), t);
                    } else {
                        t = buildFloor(temp, target, t.getX() - 1, t.getY(), t);
                    }
                } else {
                    if (t.getX() == cellSize - 1) {
                        temp = cells[(temp.getxCellCoord() + 1) * cellAmountDirection + temp.getyCellCoord()];
                        t = buildFloor(temp, target, 0, t.getY(), t);
                    } else {
                        t = buildFloor(temp, target, t.getX() + 1, t.getY(), t);
                    }
                }
                if (t == null || t.isConnectedTo(target)) {
                    break;
                }
            }
            target.setConnected();
            target.connectTo(cell);
            cell.connectTo(target);
            c.remove(target);
            cell = target;
        }
    }

    public GeneratedTile buildFloor(Cell temp, Cell target, int x, int y, GeneratedTile t) {

        GeneratedTile t2 = temp.getCellTiles()[x][y];
        if (t2 != null) {
            if (t2.getType().startsWith("wall")) {
                int l = 4;
            }
            if (t2.isConnectedTo(target)) {
                t2.connectTo(t);
                t.connectTo(t2);
                return t2;
            } else if (!t2.collides()) {

                t2.connectTo(t);
                t.connectTo(t2);
                temp.setConnected();
                return t2;
            }

        }
        t2 = new GeneratedTile(x, y, "floor1", false, null,null);
        temp.getCellTiles()[x][y] = t2;
        t2.connectTo(t);
        t.connectTo(t2);

        t = t2;
        return t;
    }



    @Override
    public synchronized void clearUpdates() {
        updates.clear();
    }

    @Override
    public void addGameListener(GameListener gli) {
        glis.add(gli);
    }

    @Override
    public Tile getTile(int x, int y) {
        if (x < 0 || y < 0 || x >= tiles.length || y >= tiles[x].length) {
            return null;
        }
        return tiles[x][y];
    }

    @Override
    public int getFocusX() {
        return focusX;
    }

    @Override
    public int getFocusY() {
        return focusY;
    }

    @Override
    public synchronized boolean move(GameObject obj, int direction) {
        Tile fromPos = getTile(obj.getX(), obj.getY());
        Tile toPos = getTile(obj.getX() + moves[direction][0], obj.getY() + moves[direction][1]);
        if (toPos != null && !toPos.collides()) {
            List<GameObject> tileCont = toPos.getContents();
            Iterator<GameObject> it = tileCont.iterator();
            while (it.hasNext()) {
                GameObject ob = it.next();
                if (ob.collides()) {
                    return false;
                }
            }
            fromPos.remove(obj);
            toPos.add(obj);
            updates.add(new Move(fromPos, toPos, obj));
            return true;
        }
        return false;
    }

    @Override
    public int getMapHeight() {
        return gridSize;
    }

    @Override
    public int getMapWidth() {
        return tiles.length;
    }

    @Override
    public GameObjectRegister getObjectRegister() {
        return register;
    }

    @Override
    public void removeObject(GameObject obj) {
        tiles[obj.getX()][obj.getY()].remove(obj);
        updates.add(new Move(tiles[obj.getX()][obj.getY()], null, obj));
        if(obj instanceof PlayerChar)
        {
            decPlayers();
        }
    }

    @Override
    public Tile getStartTile() {
        return startTile;
    }

    @Override
    public void setUpdateList(List<Move> move) {
        this.updates = move;
    }
    @Override
    public List<Move> getTileUpdates()
    {
        return updates;
    }

    @Override
    public void incPlayers() {
        nplayers++;
        gh.getAIController().setActive(this);
    }
    
    @Override
    public void decPlayers()
    {
        nplayers--;
        if(nplayers<=0)
        {
            gh.getAIController().setInactive(this);
        }
    }

    @Override
    public Gameworld getNextLevel() {
        return next;
    }
    public void setNext(Gameworld n)
    {
        next = n;
    }
    public int getDepth()
    {
        return dp;
    }
}
